package model

import (
	"database/sql"
	"encoding/json"
	"math"
	"time"
	"wxpay/pkg/util"

	"gorm.io/datatypes"
)

/*
DB Table Details
-------------------------------------


Table: trade
[ 0] id                                             INT8                 null: false  primary: true   isArray: false  auto: false  col: INT8            len: -1      default: []
[ 1] brand_id                                       VARCHAR(16)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 16      default: []
[ 2] mch_id                                         VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
[ 3] sub_mch_id                                     VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
[ 4] appid                                          VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
[ 5] sub_appid                                      VARCHAR(32)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
[ 6] out_trade_no                                   VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
[ 7] expire_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
[ 8] settle_info                                    JSONB                null: false  primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
[ 9] amount_info                                    JSONB                null: false  primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
[10] payer_info                                     JSONB                null: false  primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
[11] scene_info                                     VARCHAR(255)         null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 255     default: []
[12] description                                    VARCHAR(128)         null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 128     default: []
[13] extra                                          JSONB                null: true   primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
[14] prepay_id                                      VARCHAR(64)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 64      default: []
[15] wx_trans_id                                    VARCHAR(32)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
[16] trade_type                                     VARCHAR(16)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 16      default: []
[17] trade_state                                    VARCHAR(32)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
[18] trade_state_desc                               VARCHAR(255)         null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 255     default: []
[19] success_time                                   TIMESTAMPTZ          null: true   primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
[20] create_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
[21] update_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []


JSON Sample
-------------------------------------
{    "appid": "UrkOOBbEHlIZLuCmTpDAOKQWZ",    "out_trade_no": "MhxqNIXKiTOXUEbMyGVGGAmty",    "expire_time": "2120-12-29T23:26:41.462638675+08:00",    "scene_info": "NRIryTSLlxGnQUToImaUlRiEX",    "description": "gLnMOuSfHNCVhWsQdOUwKheYL",    "wx_trans_id": "pqZEKdboVWwIdODKjiGvxgDTE",    "trade_type": "tPCqWjkoEOxspBGCdxUcMlDwi",    "create_time": "2105-12-21T14:15:02.068413131+08:00",    "brand_id": "swQhEpaAEIEVJDwxQNKXBWxxu",    "mch_id": "cptaEnMZqpUQuYURTNcqNdqPG",    "amount_info": "WeadsSiWnGGuLEhqsQRsYvijE",    "settle_info": "bhsKxPBkDtJPXkyMjpbsemijU",    "trade_state": "HUywVXmppRoDpxHVnKdnHYgsL",    "success_time": "2286-03-22T15:01:05.97698811+08:00",    "id": 69,    "sub_mch_id": "AnMFeBZPIqyLAhcpUOgxWjVmI",    "sub_appid": "tCDrLxBuMSAEEyLtcjUkAtBOa",    "payer_info": "xITpkxINPgspSAjOBdpIKZVlT",    "extra": "vJNIWpJhoGLLWAqvrwEtSxdwy",    "prepay_id": "wGecxKbjaHcdhVTxevyLjainR",    "trade_state_desc": "ZenNxKkhSExqmBieoHZLiOQeA",    "update_time": "2144-12-21T19:59:24.146922963+08:00"}



*/

// Trade struct is a row record of the trade table in the wxpay database
type Trade struct {
	//[ 0] id                                             INT8                 null: false  primary: true   isArray: false  auto: false  col: INT8            len: -1      default: []
	ID int64 `gorm:"primary_key;column:id;type:INT8;"`
	//[ 1] brand_id                                       VARCHAR(16)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 16      default: []
	BrandID string `gorm:"column:brand_id;type:VARCHAR;size:16;"`
	//[ 2] mch_id                                         VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	MchID string `gorm:"column:mch_id;type:VARCHAR;size:32;"`
	//[ 3] sub_mch_id                                     VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	SubMchID string `gorm:"column:sub_mch_id;type:VARCHAR;size:32;"`
	//[ 4] appid                                          VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	AppID string `gorm:"column:appid;type:VARCHAR;size:32;"`
	//[ 5] sub_appid                                      VARCHAR(32)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	SubAppID sql.NullString `gorm:"column:sub_appid;type:VARCHAR;size:32;"`
	//[ 6] out_trade_no                                   VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	OutTradeNo string `gorm:"column:out_trade_no;type:VARCHAR;size:32;"`
	//[ 7] expire_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
	ExpireTime time.Time `gorm:"column:expire_time;type:TIMESTAMPTZ;"`
	//[ 8] settle_info                                    JSONB                null: false  primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
	SettleInfo datatypes.JSON `gorm:"column:settle_info;type:JSONB;"`
	//[ 9] amount_info                                    JSONB                null: false  primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
	AmountInfo datatypes.JSON `gorm:"column:amount_info;type:JSONB;"`
	//[10] payer_info                                     JSONB                null: false  primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
	PayerInfo datatypes.JSON `gorm:"column:payer_info;type:JSONB;"`
	//[11] scene_info                                     JSONB         null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 255     default: []
	SceneInfo datatypes.JSON `gorm:"column:scene_info;type:VARCHAR;size:255;"`
	//[12] description                                    VARCHAR(128)         null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 128     default: []
	Description string `gorm:"column:description;type:VARCHAR;size:128;"`
	//[13] extra                                          JSONB                null: true   primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
	Extra datatypes.JSON `gorm:"column:extra;type:JSONB;"`
	//[14] prepay_id                                      VARCHAR(64)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 64      default: []
	PrepayID sql.NullString `gorm:"column:prepay_id;type:VARCHAR;size:64;"`
	//[15] wx_trans_id                                    VARCHAR(32)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	WxTransID sql.NullString `gorm:"column:wx_trans_id;type:VARCHAR;size:32;"`
	//[16] trade_type                                     VARCHAR(16)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 16      default: []
	TradeType sql.NullString `gorm:"column:trade_type;type:VARCHAR;size:16;"`
	//[17] trade_state                                    VARCHAR(32)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	TradeState sql.NullString `gorm:"column:trade_state;type:VARCHAR;size:32;"`
	//[18] trade_state_desc                               VARCHAR(255)         null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 255     default: []
	TradeStateDesc sql.NullString `gorm:"column:trade_state_desc;type:VARCHAR;size:255;"`
	//[19] success_time                                   TIMESTAMPTZ          null: true   primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
	SuccessTime *time.Time `gorm:"column:success_time;type:TIMESTAMPTZ;"`
	//[20] create_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
	CreateTime time.Time `gorm:"column:create_time;type:TIMESTAMPTZ;"`
	//[21] update_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
	UpdateTime time.Time `gorm:"column:update_time;type:TIMESTAMPTZ;"`
}

// TableName sets the insert table name for this struct type
func (t *Trade) TableName() string {
	return "trade"
}

type SettleInfo struct {
	// 是否指定分账
	ProfitSharing bool `json:"profit_sharing"`
	// 补差金额
	SubsidyAmount int `json:"subsidy_amount"`
}

func (info *SettleInfo) ToJson() (r datatypes.JSON) {
	r, _ = json.Marshal(info)
	return
}

func (info *SettleInfo) ToStruct(r datatypes.JSON) {
	b, _ := r.MarshalJSON()
	_ = json.Unmarshal(b, info)
}

type AmountInfo struct {
	// 总金额
	Total uint `json:"total"`
	// 货币类型
	Currency string `json:"currency"`
	// 用户支付金额
	PayerTotal uint `json:"payer_total"`
	// 用户支付币种
	PayerCurrency string `json:"payer_currency"`
}

func (info *AmountInfo) ToJson() (r datatypes.JSON) {
	r, _ = json.Marshal(info)
	return
}

func (info *AmountInfo) ToStruct(r datatypes.JSON) {
	b, _ := r.MarshalJSON()
	_ = json.Unmarshal(b, info)
}

type Payer struct {
	// 用户服务标识
	SpOpenID string `json:"sp_openid"`
	// 用户子标识
	SubOpenID string `json:"sub_openid"`
}

func (info *Payer) ToJson() (r datatypes.JSON) {
	r, _ = json.Marshal(info)
	return
}

func (info *Payer) ToStruct(r datatypes.JSON) {
	b, _ := r.MarshalJSON()
	_ = json.Unmarshal(b, info)
}

type SceneInfo struct {
	// 用户终端IP
	PayerClientIP string `json:"payer_client_ip"`
	// 商户端设备号
	DeviceID string `json:"device_id"`
	// 商户门店信息
	StoreInfo struct {
		// 门店编号
		ID string `json:"id"`
		// 门店名称
		Name string `json:"name"`
		// 地区编码
		AreaCode string `json:"area_code"`
		// 详细地址
		Address string `json:"address"`
	} `json:"store_info"`
}

func (info *SceneInfo) ToJson() (r datatypes.JSON) {
	r, _ = json.Marshal(info)
	return
}

func (info *SceneInfo) ToStruct(r datatypes.JSON) {
	b, _ := r.MarshalJSON()
	_ = json.Unmarshal(b, info)
}

func (t *Trade) SetPrepayID(prepayID string) {
	if prepayID == "" {
		t.PrepayID = sql.NullString{}
	} else {
		t.PrepayID = sql.NullString{
			String: prepayID,
			Valid:  true,
		}
	}

}

// 检查品牌归属
func (t *Trade) DoesBelongTo(brandID string) (belong bool) {
	return brandID == t.BrandID
}

// 更新支付信息
func (t *Trade) UpdateVoucher(tType string, tState string, tStateDesc string, txID string, successT *time.Time) {
	t.TradeType = util.AdaptNullString(tType)
	t.TradeState = util.AdaptNullString(tState)
	t.TradeStateDesc = util.AdaptNullString(tStateDesc)
	t.WxTransID = util.AdaptNullString(txID)
	t.UpdateTime = util.GetNow()
	// 成功支付需要更新成功时间和服务费
	if successT != nil {
		t.SuccessTime = successT
		serviceFee := t.CalculateServiceFee()
		extra := t.GetExtra()
		extra.ServiceFee = &serviceFee
		t.Extra = extra.ToJson()
	}
}

// 关闭交易
func (t *Trade) Close() (err error) {
	t.TradeState = util.AdaptNullString("CLOSED")
	t.TradeStateDesc = util.AdaptNullString("已关闭")
	return
}

// 是否终态判断
func (t *Trade) IsFinalState() bool {
	switch util.ConvertNullString(t.TradeState) {
	case "SUCCESS", "CLOSED", "PAYERROR", "REVOKED":
		return true
	}
	return false
}

// 订单总金额
func (t *Trade) GetTotal() int64 {
	tradeAmount := AmountInfo{}
	tradeAmount.ToStruct(t.AmountInfo)
	return int64(tradeAmount.Total)
}

func (t *Trade) GetAmountInfo() AmountInfo {
	tradeAmount := AmountInfo{}
	tradeAmount.ToStruct(t.AmountInfo)
	return tradeAmount
}

// 服务费
type tradeExtra struct {
	// 费率*100000 千分之六=60
	Rate int64 `json:"rate"`
	// 服务费
	ServiceFee *int64 `json:"service_fee"`
}

func NewTradeExtra(rate int64) *tradeExtra {
	return &tradeExtra{
		Rate: rate,
	}
}

func (info *tradeExtra) ToJson() (r datatypes.JSON) {
	r, _ = json.Marshal(info)
	return
}

func (info *tradeExtra) ToStruct(r datatypes.JSON) {
	b, _ := r.MarshalJSON()
	_ = json.Unmarshal(b, info)
}

// 获取服务费率
func (t *Trade) GetServiceRate() int64 {
	extra := t.GetExtra()
	return extra.Rate
}

// 订单服务费
func (t *Trade) GetServiceFee() *int64 {
	extra := t.GetExtra()
	return extra.ServiceFee
}

// 订单服务费 结算额*费率
func (t *Trade) CalculateServiceFee() int64 {
	// 四舍五入
	return int64(math.Round(float64(t.GetServiceRate()*t.GetTotal()) / 10000))
}

func (t *Trade) GetExtra() tradeExtra {
	extra := tradeExtra{}
	extra.ToStruct(t.Extra)
	return extra
}
